﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="MainForm.cs" company="Elilink">
//   Defor
// </copyright>
// <summary>
//   Main form class
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace Deployment
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Xml.Linq;

    using BusinessLayer.Objects;
    using BusinessLayer.Objects.BackgroundWorkerObjects;
    using BusinessLayer.Parsers;
    using BusinessLayer.Results;
    using BusinessLayer.Tools;

    using DataAccessLayer.Repositories.Abstract;

    using Deployment.Properties;
    using Deployment.Tools;

    using Microsoft.Build.Evaluation;
    using Microsoft.Build.Execution;
    using Microsoft.Build.Framework;
    using Microsoft.Build.Logging;

    /// <summary>
    ///     Main form class
    /// </summary>
    public partial class MainForm : Form
    {
        #region Constants

        /// <summary>
        ///     The pinsol file name
        /// </summary>
        private const string PinsolFileName = "PINSOL.xml";

        #endregion

        #region Fields

        /// <summary>
        ///     The Deployable project repository
        /// </summary>
        private readonly IDeployableProjectRepository _deployableProjectRepository;

        /// <summary>
        ///     The Target system repository
        /// </summary>
        private readonly ITargetSystemRepository _targetSystemRepository;

        /// <summary>
        ///     The application path
        /// </summary>
        private string _applicationPath;

        /// <summary>
        ///     The _changed after deploy start.
        /// </summary>
        private bool _changedAfterDeployStart;

        /// <summary>
        ///     Field for saving last path of imported file to save to
        /// </summary>
        private string _lastUsedFilePath;

        /// <summary>
        ///     Boolean flag to determine if project has been changed
        /// </summary>
        private bool _needSave;

        /// <summary>
        ///     List of selected projects to save/restore selected status after sorting
        /// </summary>
        private List<string> _selectedProjectsNames;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="MainForm"/> class.
        /// </summary>
        /// <param name="targetSystemRepository">
        /// The target system repository.
        /// </param>
        /// <param name="deployableProjectRepository">
        /// The deployable project repository.
        /// </param>
        public MainForm(
            ITargetSystemRepository targetSystemRepository, 
            IDeployableProjectRepository deployableProjectRepository)
        {
            InitializeComponent();
            _deployableProjectRepository = deployableProjectRepository;
            _targetSystemRepository = targetSystemRepository;
        }

        #endregion

        #region Properties

        /// <summary>
        ///     Gets the current build configuration name.
        /// </summary>
        /// <value>The build configuration.</value>
        private string BuildConfiguration
        {
            get
            {
                if (CurrentTarget == null)
                {
                    return string.Empty;
                }

                return CurrentTarget.BuildConfiguration;
            }
        }

        /// <summary>
        ///     Gets the current target.
        /// </summary>
        /// <value>The current target.</value>
        private TargetSystem CurrentTarget
        {
            get
            {
                if (cbxTarget.SelectedValue == null)
                {
                    return new TargetSystem();
                }

                TargetSystem target =
                    _targetSystemRepository.GetTargetSystems()
                        .First(t => t.TargetSystemName == (string)cbxTarget.SelectedValue)
                        .DeepCopy();
                foreach (DeployableProject deployableProject in target.DeployableProjects)
                {
                    if (!string.IsNullOrEmpty(deployableProject.ProjectName)
                        && !string.IsNullOrEmpty(deployableProject.ProjectPath)
                        && !string.IsNullOrEmpty(deployableProject.PublishProfile))
                    {
                        deployableProject.ReadyForDeploy = true;
                    }
                    else
                    {
                        deployableProject.ReadyForDeploy = false;
                    }
                }

                return target;
            }
        }

        /// <summary>
        ///     Gets the list of all projects selected for deployment.
        /// </summary>
        /// <value>The deployable projects.</value>
        private List<DeployableProject> DeployableProjects
        {
            get
            {
                List<DeployableProject> projects =
                    CurrentTarget.DeployableProjects.Where(p => SelectedProjectNames.Contains(p.ProjectName))
                        .ToList();

                return projects;
            }
        }

        /// <summary>
        ///     Gets the absolute path to logs folder.
        /// </summary>
        /// <value>The logs path.</value>
        private string LogsPath
        {
            get
            {
                return PathesHelper.FixLogsPath(txtLogsFolder.Text, false);
            }
        }

        /// <summary>
        ///     Gets the selected project names.
        /// </summary>
        /// <value>The selected project names.</value>
        private List<string> SelectedProjectNames
        {
            get
            {
                return (from DataGridViewRow row in gridProjects.Rows
                        where row.Index >= 0
                        let checkBoxCell = row.Cells[0]
                        where checkBoxCell.Value != null && (bool)checkBoxCell.Value
                        select (string)row.Cells[2].Value).ToList();
            }
        }

        /// <summary>
        ///     Gets the absolute path to solution folder.
        /// </summary>
        /// <value>The solution path.</value>
        private string SolutionPath
        {
            get
            {
                return PathesHelper.FixSolutionPath(txtSolutionFolder.Text);
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Handles the Load event of the MainForm control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="EventArgs"/> instance containing the event data.
        /// </param>
        internal void MainForm_Load(object sender, EventArgs e)
        {
            SetProgressStatus("Waiting for start", Color.Black, false);
            gridProjects.AutoGenerateColumns = false;
            _applicationPath = PathesHelper.FixSolutionPath(Path.GetDirectoryName(Application.ExecutablePath));
            txtSolutionFolder.Text = _applicationPath;
            NeedSaveStatusChange(false);
        }

        /// <summary>
        /// Handles the DoWork event of the BackgroundWorker control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="DoWorkEventArgs"/> instance containing the event data.
        /// </param>
        private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            int success = 0;
            int failure = 0;
            var worker = (BackgroundWorker)sender;
            var input = (BackgroundWorkerInputData)e.Argument;
            string solutionPath = input.SolutionPath;
            string logsPath = input.LogsPath;
            List<DeployableProject> deployableProjects = input.DeployableProjects;
            foreach (DeployableProject deployableProject in deployableProjects)
            {
                if (!File.Exists(deployableProject.ProjectPath))
                {
                    deployableProject.ProjectPath =
                        string.Concat(solutionPath, deployableProject.ProjectPath).Replace(@"\\", @"\");
                }
            }

            string buildConfiguration = input.BuildConfiguration;
            int count = deployableProjects.Count;
            for (int i = 0; i < count; i++)
            {
                if (worker.CancellationPending)
                {
                    bgw.ReportProgress(
                        100, 
                        new BackgroundWorkerReport
                            {
                                IsTotal = true, 
                                Success = success, 
                                Failure = failure, 
                                Skipped = count - success - failure, 
                                Aborted = true
                            });
                    return;
                }

                int persentage = i * 100 / count;
                bgw.ReportProgress(persentage, new BackgroundWorkerReport { IsStartProject = true });
                string initialText = string.Format(
                    "==============Project {0} ({1} of {2})==============", 
                    deployableProjects[i].ProjectName, 
                    i + 1, 
                    deployableProjects.Count());
                bgw.ReportProgress(persentage, new BackgroundWorkerReport { Text = initialText, AddSpace = false });

                string projectDescription = string.Format(
                    "Project path: {0}; publish profile: {1}; configuration: {2}", 
                    deployableProjects[i].ProjectPath, 
                    deployableProjects[i].PublishProfile, 
                    buildConfiguration);
                bgw.ReportProgress(
                    persentage, 
                    new BackgroundWorkerReport { Text = projectDescription, AddSpace = false });
                var pc = new ProjectCollection();
                var buildParameters = new BuildParameters(pc);
                string logFilePath = string.Format(
                    @"{0}{1}_{2}_DeployLog.log", 
                    logsPath, 
                    DateTime.Now.ToString("yy.MM.dd_HH.mm.ss"), 
                    deployableProjects[i].ProjectName);
                bgw.ReportProgress(
                    persentage, 
                    new BackgroundWorkerReport
                        {
                            Text = string.Format("Log will be written to file: {0}", logFilePath), 
                            AddSpace = true
                        });
                var fileLogger = new FileLogger { Parameters = string.Format("logfile={0}", logFilePath) };
                var customFileLogger = new CustomFileLogger { Parameters = string.Format("logfile={0}", @"d:\log2.log") };
                /*var customLogger = new CustomLogger { Parameters = @"d:\log.log" };*/
                var consoleLogger = new ConsoleLogger();
                buildParameters.Loggers = new List<ILogger> { fileLogger, consoleLogger, /*customLogger, */customFileLogger };
                var globalProperty = new Dictionary<string, string>
                                         {
                                             { "Configuration", buildConfiguration }, 
                                             { "DeployOnBuild", "true" }, 
                                             {
                                                 "PublishProfile", 
                                                 deployableProjects[i].PublishProfile
                                             }, 
                                             { "SolutionDir", solutionPath }, 
                                             { "UserName", deployableProjects[i].UserName }, 
                                             { "Password", deployableProjects[i].Password }, 
                                             { "Platform", "AnyCPU" }
                                         };

                var buildRequest = new BuildRequestData(
                    deployableProjects[i].ProjectPath, 
                    globalProperty, 
                    null, 
                    new[] { "Build" }, 
                    null);

                // TODO Think about adding async calls!!!
                /*BuildManager.DefaultBuildManager.BeginBuild(buildParameters);
                BuildSubmission submission = BuildManager.DefaultBuildManager.PendBuildRequest(buildRequest);
                submission.ExecuteAsync(null, null);
                int cpt = 0;
                while (!submission.IsCompleted)
                {
                    cpt++;
                    bgw.ReportProgress(
                    persentage,
                    new BackgroundWorkerReport
                    {
                        Text = string.Format("Pending build: {0}", cpt),
                        AddSpace = true
                    });
                }


                BuildManager.DefaultBuildManager.EndBuild();
                // If the build failed, return an error string.    
                if (submission.BuildResult.OverallResult == BuildResultCode.Failure)
                {                 //do some error task           
                } */
                try
                {
                    BuildResult buildResult = BuildManager.DefaultBuildManager.Build(buildParameters, buildRequest);
                    bool result = buildResult.OverallResult == BuildResultCode.Success;
                    if (result)
                    {
                        success++;
                    }
                    else
                    {
                        failure++;
                    }

                    bgw.ReportProgress(
                        persentage, 
                        new BackgroundWorkerReport
                            {
                                Text =
                                    string.Format(
                                        "Project '{0}' - {1}", 
                                        deployableProjects[i].ProjectName, 
                                        result ? "Operation success" : "Operation fails"), 
                                IsColored = true, 
                                Color = result ? Color.Green : Color.Red, 
                                ProjectName = deployableProjects[i].ProjectName
                            });
                }
                catch (Exception ex)
                {
                    bgw.ReportProgress(
                        persentage, 
                        new BackgroundWorkerReport
                            {
                                Text =
                                    string.Format(
                                        "Project '{1}' - Operation fails with exception: {0}", 
                                        ex.Message, 
                                        deployableProjects[i].ProjectName), 
                                IsColored = true, 
                                Color = Color.Red, 
                            });
                    failure++;
                }
            }

            bgw.ReportProgress(
                100, 
                new BackgroundWorkerReport { IsTotal = true, Success = success, Failure = failure, Skipped = 0 });
        }

        /// <summary>
        /// Handles the ProgressChanged event of the BackgroundWorker control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="ProgressChangedEventArgs"/> instance containing the event data.
        /// </param>
        private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            var report = (BackgroundWorkerReport)e.UserState;
            prBar.Value = e.ProgressPercentage;
            SetProgressStatus(
                string.Format("Completed {0}%. Working...", e.ProgressPercentage), 
                Color.Green, 
                false);
            if (report.IsStartProject)
            {
                if (txtResults.Lines.Length > 0)
                {
                    SetText(string.Empty, false);
                }

                return;
            }

            if (!report.IsTotal)
            {
                if (!report.IsColored)
                {
                    SetText(report.Text, report.AddSpace);
                    return;
                }

                SetColoredText(report.Color, report.Text, false);
                if (report.Color == Color.Green && !_changedAfterDeployStart)
                {
                    DeselectCompletedProject(report.ProjectName);
                }

                return;
            }

            SetTotalResults(report.Success, report.Failure, report.Skipped);
            SetProgressStatus("Operations success", Color.Green, true);
            if (report.Aborted)
            {
                SetText(string.Empty, true);
                SetColoredText(Color.Red, "Aborted by user!!", false);
                SetProgressStatus("Aborted by user", Color.Red, true);
            }
        }

        /// <summary>
        /// Handles the RunWorkerCompleted event of the BackgroundWorker control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="RunWorkerCompletedEventArgs"/> instance containing the event data.
        /// </param>
        private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            btnDeploy.Text = @"Deploy";
            FlashWindow.Flash(this, 5);
        }

        /// <summary>
        ///     Binds the data.
        /// </summary>
        private void BindData()
        {
            SaveCurrentSelectedRows();
            var dataSource = new SortableBindingList<DeployableProject>(CurrentTarget.DeployableProjects);
            gridProjects.SelectionMode = DataGridViewSelectionMode.RowHeaderSelect;
            gridProjects.DataSource = dataSource;
            gridProjects.ClearSelection();

            // PathColumn.Width = gridProjects.RowCount > 6 ? 413 : 430;
            PathColumn.DataPropertyName = "ProjectPath";
            ProjectNameColumn.DataPropertyName = "ProjectName";
            PublishProfileColumn.DataPropertyName = "PublishProfile";
            foreach (DataGridViewRow row in gridProjects.Rows)
            {
                row.Cells[SelectionColumn.Name].Value = false;
                SetStatusCell(row);
            }

            RestoreSelectedRows();
        }

        /// <summary>
        /// Binds the target systems.
        /// </summary>
        /// <param name="newSelectedValue">
        /// The new selected value.
        /// </param>
        private void BindTargetSystems(string newSelectedValue = "")
        {
            List<TargetSystem> currentTargets = _targetSystemRepository.GetTargetSystems();
            if (currentTargets.Any())
            {
                string currentSelectedValue = newSelectedValue;
                if (cbxTarget.SelectedValue != null && string.IsNullOrEmpty(currentSelectedValue))
                {
                    currentSelectedValue = cbxTarget.SelectedValue.ToString();
                }

                cbxTarget.DataSource = currentTargets.Select(t => t.TargetSystemName).OrderBy(t => t).ToList();
                if (!string.IsNullOrEmpty(currentSelectedValue))
                {
                    int index = cbxTarget.Items.IndexOf(currentSelectedValue);
                    if (index >= 0)
                    {
                        cbxTarget.SelectedIndex = index;
                    }
                }
            }
            else
            {
                cbxTarget.DataSource = currentTargets;
                BindData();
            }
        }

        /// <summary>
        /// Handles the Click event of the BtnAddProject control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="EventArgs"/> instance containing the event data.
        /// </param>
        private void BtnAddProject_Click(object sender, EventArgs e)
        {
            if (cbxTarget.SelectedValue == null)
            {
                MessageBoxEx.Show(
                    this, 
                    "At least one target should be created before", 
                    "Failed", 
                    MessageBoxButtons.OK, 
                    MessageBoxIcon.Error);
                return;
            }

            var createProjectDialog = new CreateEditProjectForm(
                _deployableProjectRepository, 
                _targetSystemRepository, 
                cbxTarget.SelectedValue.ToString());
            OperationResult result = createProjectDialog.ShowCreateDialog(this);
            createProjectDialog.Dispose();
            if (result.Result != null)
            {
                NeedSaveStatusChange(true);
                BindTargetSystems((string)result.Result);
                _changedAfterDeployStart = true;
            }
        }

        /// <summary>
        /// Handles the Click event of the BtnCreateTarget control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="EventArgs"/> instance containing the event data.
        /// </param>
        private void BtnCreateTarget_Click(object sender, EventArgs e)
        {
            var createTargetDialog = new CreateEditTargetForm(_targetSystemRepository);
            OperationResult result = createTargetDialog.ShowCreateDialog(this);
            createTargetDialog.Dispose();
            if ((bool)result.Result)
            {
                NeedSaveStatusChange(true);
                BindTargetSystems();
                _changedAfterDeployStart = true;
            }
        }

        /// <summary>
        /// Handles the Click event of the BtnDeploy control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="EventArgs"/> instance containing the event data.
        /// </param>
        private void BtnDeploy_Click(object sender, EventArgs e)
        {
            if (bgw.IsBusy)
            {
                bgw.CancelAsync();
                SetProgressStatus("Canceling...", Color.Red, true);
                return;
            }

            if (!CheckInputParameters())
            {
                return;
            }

            string messageBoxMessage =
                string.Format(
                    "Before processing, please, check input data!\n\nTarget - '{0}'; To deploy - {1} project(s)\n\nSure want to process deploy?", 
                    cbxTarget.SelectedValue, 
                    DeployableProjects.Count);
            if (MessageBoxEx.Show(
                this, 
                messageBoxMessage, 
                "Confirmation", 
                MessageBoxButtons.YesNo, 
                MessageBoxIcon.Information) != DialogResult.Yes)
            {
                return;
            }

            btnDeploy.Text = @"Cancel";
            prBar.Value = 0;
            var inputData = new BackgroundWorkerInputData
                                {
                                    DeployableProjects = DeployableProjects, 
                                    LogsPath = LogsPath, 
                                    SolutionPath = SolutionPath, 
                                    BuildConfiguration = BuildConfiguration
                                };
            _changedAfterDeployStart = false;
            bgw.RunWorkerAsync(inputData);
        }

        /// <summary>
        /// Handles the Click event of the BtnDeselectAll control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="EventArgs"/> instance containing the event data.
        /// </param>
        private void BtnDeselectAll_Click(object sender, EventArgs e)
        {
            foreach (DataGridViewRow row in gridProjects.Rows)
            {
                if (row.Index < 0)
                {
                    continue;
                }

                DataGridViewCell chechboxCell = row.Cells[0];
                chechboxCell.Value = false;
            }

            _changedAfterDeployStart = true;
        }

        /// <summary>
        /// Handles the Click event of the BtnEditCurrentTarget control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="EventArgs"/> instance containing the event data.
        /// </param>
        private void BtnEditCurrentTarget_Click(object sender, EventArgs e)
        {
            if (cbxTarget.SelectedValue == null)
            {
                BtnCreateTarget_Click(sender, e);
                return;
            }

            var editTargetDialog = new CreateEditTargetForm(_targetSystemRepository, CurrentTarget);
            OperationResult result = editTargetDialog.ShowEditDialog(this);
            editTargetDialog.Dispose();
            if (result.Result != null)
            {
                NeedSaveStatusChange(true);
                BindTargetSystems((string)result.Result);
                _changedAfterDeployStart = true;
            }
        }

        /// <summary>
        /// Handles the Click event of the BtnFindSolutionFolder control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="EventArgs"/> instance containing the event data.
        /// </param>
        private void BtnFindSolutionFolder_Click(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(SolutionPath))
            {
                dlgFolderBrowserDialog.SelectedPath = SolutionPath;
            }

            if (dlgFolderBrowserDialog.ShowDialog(this) == DialogResult.OK)
            {
                txtSolutionFolder.Text = dlgFolderBrowserDialog.SelectedPath;
            }
        }

        /// <summary>
        /// Handles the Click event of the BtnGetSample control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="EventArgs"/> instance containing the event data.
        /// </param>
        private void BtnGetSample_Click(object sender, EventArgs e)
        {
            string file = Resources.Template;
            SaveFileAs(file, true);
        }

        /// <summary>
        /// Handles the Click event of the BtnImportFromXML control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="EventArgs"/> instance containing the event data.
        /// </param>
        private void BtnImportFromXML_Click(object sender, EventArgs e)
        {
            SetInitialPositionForOpenDialog();
            if (dlgOpenFile.ShowDialog(this) == DialogResult.OK)
            {
                string path = dlgOpenFile.FileName;
                _lastUsedFilePath = path;
                string file = File.ReadAllText(path);
                ImportFromFile(file);
            }
        }

        /// <summary>
        /// Handles the Click event of the BtnImportPinsolDefaults control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="EventArgs"/> instance containing the event data.
        /// </param>
        private void BtnImportPinsolDefaults_Click(object sender, EventArgs e)
        {
            var filePathAndName = string.Concat(_applicationPath, PinsolFileName);
            if (File.Exists(filePathAndName))
            {
                string file = File.ReadAllText(filePathAndName);
                ImportFromFile(file);
            }
            else
            {
                BtnImportFromXML_Click(null, null);
            }
        }

        /// <summary>
        /// Handles the Click event of the BtnInvertSelection control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="EventArgs"/> instance containing the event data.
        /// </param>
        private void BtnInvertSelection_Click(object sender, EventArgs e)
        {
            foreach (DataGridViewRow row in gridProjects.Rows)
            {
                if (row.Index < 0)
                {
                    continue;
                }

                DataGridViewCell chechboxCell = row.Cells[0];
                if (chechboxCell.Value == null)
                {
                    chechboxCell.Value = false;
                    return;
                }

                chechboxCell.Value = !(bool)chechboxCell.Value;
            }

            _changedAfterDeployStart = true;
        }

        /// <summary>
        /// Handles the Click event of the BtnRemoveAll control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="EventArgs"/> instance containing the event data.
        /// </param>
        private void BtnRemoveAll_Click(object sender, EventArgs e)
        {
            _targetSystemRepository.DeleteAllTargetSystems();
            txtSolutionFolder.Text = Path.GetDirectoryName(Application.ExecutablePath);
            NeedSaveStatusChange(false);
            BindTargetSystems();
            _changedAfterDeployStart = true;
        }

        /// <summary>
        /// Handles the Click event of the BtnRemoveSelectedProjects control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="EventArgs"/> instance containing the event data.
        /// </param>
        private void BtnRemoveSelectedProjects_Click(object sender, EventArgs e)
        {
            if (CurrentTarget == null)
            {
                SetText("Target could not be determined", true);
                return;
            }

            List<DeployableProject> projects = DeployableProjects;
            int deletedProjectsCount = projects.Count;
            var sb = new StringBuilder();
            foreach (DeployableProject project in projects)
            {
                OperationResult result = _deployableProjectRepository.DeleteDeployableProject(
                    project.ProjectName, 
                    CurrentTarget.TargetSystemName);
                if (result.ErrorType != 0)
                {
                    sb.AppendLine(
                        string.Format("Can't delete project {0}: {1}", project.ProjectName, result.ErrorDescription));
                    deletedProjectsCount--;
                }
            }

            if (!string.IsNullOrEmpty(sb.ToString()))
            {
                SetColoredText(Color.Red, sb.ToString(), false);
            }

            SetText(string.Format("Successfully deleted {0} project(s)", deletedProjectsCount), true);
            NeedSaveStatusChange(true);
            _changedAfterDeployStart = true;
            BindData();
        }

        /// <summary>
        /// Handles the Click event of the BtnRemoveTarget control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="EventArgs"/> instance containing the event data.
        /// </param>
        private void BtnRemoveTarget_Click(object sender, EventArgs e)
        {
            if (CurrentTarget == null)
            {
                SetText("Target could not be determined", true);
                return;
            }

            _targetSystemRepository.DeleteTargetSystem(CurrentTarget.TargetSystemName);
            NeedSaveStatusChange(true);
            BindTargetSystems();
            _changedAfterDeployStart = true;
        }

        /// <summary>
        /// Handles the Click event of the BtnSaveToFile control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="EventArgs"/> instance containing the event data.
        /// </param>
        private void BtnSaveToFile_Click(object sender, EventArgs e)
        {
            var model = new XmlDocumentModel
                            {
                                LogsPath = PathesHelper.FixSolutionPath(txtLogsFolder.Text), 
                                SolutionPath = PathesHelper.FixSolutionPath(txtSolutionFolder.Text), 
                                TargetSystems = _targetSystemRepository.GetTargetSystems()
                            };
            string file = XmlParser.CreateOutputFile(model);
            SaveFileAs(file, false);
        }

        /// <summary>
        /// Handles the Click event of the BtnSelectAll control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="EventArgs"/> instance containing the event data.
        /// </param>
        private void BtnSelectAll_Click(object sender, EventArgs e)
        {
            foreach (DataGridViewRow row in gridProjects.Rows)
            {
                if (row.Index < 0)
                {
                    continue;
                }

                DataGridViewCell chechboxCell = row.Cells[0];
                chechboxCell.Value = true;
            }

            _changedAfterDeployStart = true;
        }

        /// <summary>
        /// Handles the Click event of the BtnSetLogsFolder control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="EventArgs"/> instance containing the event data.
        /// </param>
        private void BtnSetLogsFolder_Click(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(LogsPath))
            {
                dlgFolderBrowserDialog.SelectedPath = LogsPath;
            }

            if (dlgFolderBrowserDialog.ShowDialog(this) == DialogResult.OK)
            {
                txtLogsFolder.Text = dlgFolderBrowserDialog.SelectedPath;
            }
        }

        /// <summary>
        /// Handles the SelectedIndexChanged event of the CbxTarget control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="EventArgs"/> instance containing the event data.
        /// </param>
        private void CbxTarget_SelectedIndexChanged(object sender, EventArgs e)
        {
            BindData();
            _changedAfterDeployStart = true;
        }

        /// <summary>
        ///     Checks the input parameters.
        /// </summary>
        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
        private bool CheckInputParameters()
        {
            if (!DeployableProjects.Any())
            {
                MessageBoxEx.Show(
                    this, 
                    "Select at least one project", 
                    "Failed", 
                    MessageBoxButtons.OK, 
                    MessageBoxIcon.Error);
                return false;
            }

            if (DeployableProjects.All(p => !p.ReadyForDeploy))
            {
                MessageBoxEx.Show(
                    this, 
                    "All selected projects are not ready for deploy. Check parameters", 
                    "Failed", 
                    MessageBoxButtons.OK, 
                    MessageBoxIcon.Error);
                return false;
            }

            if (string.IsNullOrEmpty(SolutionPath))
            {
                MessageBoxEx.Show(
                    this, 
                    "Solution folder should be specified", 
                    "Failed", 
                    MessageBoxButtons.OK, 
                    MessageBoxIcon.Error);
                return false;
            }

            if (!Directory.Exists(SolutionPath))
            {
                MessageBoxEx.Show(
                    this, 
                    "Specified solution folder does not exist", 
                    "Failed", 
                    MessageBoxButtons.OK, 
                    MessageBoxIcon.Error);
                return false;
            }

            if (string.IsNullOrEmpty(LogsPath))
            {
                MessageBoxEx.Show(
                    this, 
                    "Logs folder should be specified", 
                    "Failed", 
                    MessageBoxButtons.OK, 
                    MessageBoxIcon.Error);
                return false;
            }

            txtResults.Clear();
            if (!Directory.Exists(LogsPath))
            {
                SetColoredText(
                    Color.Red, 
                    string.Format("Directory {0} does not exist. Trying to create.", LogsPath), 
                    false);
                try
                {
                    Directory.CreateDirectory(LogsPath);
                }
                catch (Exception ex)
                {
                    SetColoredText(
                        Color.Red, 
                        string.Format("Error creating directory!! Message: {0}", ex.Message), 
                        false);
                    return false;
                }

                SetColoredText(Color.Green, "Directory successfully created.", false);
            }

            foreach (DeployableProject deployableProject in DeployableProjects)
            {
                if (!deployableProject.ReadyForDeploy)
                {
                    var message = string.Format(
                        "Skipping project '{0}'. Not all parameters specified", 
                        deployableProject.ProjectName);
                    SetColoredText(Color.Red, message, false);
                    DeselectCompletedProject(deployableProject.ProjectName);
                }
            }

            return true;
        }

        /// <summary>
        /// The deselect completed project.
        /// </summary>
        /// <param name="projectName">
        /// The project name.
        /// </param>
        private void DeselectCompletedProject(string projectName)
        {
            foreach (DataGridViewRow row in gridProjects.Rows)
            {
                if (row.Index < 0)
                {
                    continue;
                }

                if (string.Equals(row.Cells[2].Value.ToString(), projectName, StringComparison.CurrentCultureIgnoreCase))
                {
                    var checkBoxRow = row.Cells[0] as DataGridViewCheckBoxCell;
                    if (checkBoxRow != null)
                    {
                        checkBoxRow.Value = false;
                        _changedAfterDeployStart = false;
                    }

                    break;
                }
            }
        }

        /// <summary>
        /// Grids the projects cell content click.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The <see cref="DataGridViewCellEventArgs"/> instance containing the event data.
        /// </param>
        private void GridProjects_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            if (e.RowIndex < 0)
            {
                return;
            }

            if (gridProjects.Columns[e.ColumnIndex] is DataGridViewButtonColumn)
            {
                object projectName = gridProjects.Rows[e.RowIndex].Cells[2].Value;
                if (projectName == null)
                {
                    return;
                }

                DeployableProject currentProject =
                    CurrentTarget.DeployableProjects.First(p => p.ProjectName == projectName.ToString());
                var editProjectDialog = new CreateEditProjectForm(
                    _deployableProjectRepository, 
                    _targetSystemRepository, 
                    currentProject, 
                    cbxTarget.SelectedValue.ToString());
                OperationResult result = editProjectDialog.ShowEditDialog(this);
                editProjectDialog.Dispose();
                if (result.Result != null)
                {
                    NeedSaveStatusChange(true);
                    BindTargetSystems((string)result.Result);
                    _changedAfterDeployStart = true;
                }
            }
        }

        /// <summary>
        /// Handles the CellValueChanged event of the GridProjects control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="DataGridViewCellEventArgs"/> instance containing the event data.
        /// </param>
        private void GridProjects_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            if (e.ColumnIndex == 0 && e.RowIndex >= 0)
            {
                SelectAllCheckedRows();
                _changedAfterDeployStart = true;
            }
        }

        /// <summary>
        /// Handles the ColumnHeaderMouseClick event of the GridProjects control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="DataGridViewCellMouseEventArgs"/> instance containing the event data.
        /// </param>
        private void GridProjects_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
        {
            DataGridViewColumn newColumn = gridProjects.Columns[e.ColumnIndex];
            if (newColumn is DataGridViewCheckBoxColumn || newColumn is DataGridViewButtonColumn)
            {
                return;
            }

            SaveCurrentSelectedRows();
            DataGridViewColumn oldColumn = gridProjects.SortedColumn;
            ListSortDirection direction;

            // If oldColumn is null, then the DataGridView is not sorted. 
            if (oldColumn != null)
            {
                // Sort the same column again, reversing the SortOrder. 
                if (oldColumn == newColumn && gridProjects.SortOrder == SortOrder.Ascending)
                {
                    direction = ListSortDirection.Descending;
                }
                else
                {
                    // Sort a new column and remove the old SortGlyph.
                    direction = ListSortDirection.Ascending;
                    oldColumn.HeaderCell.SortGlyphDirection = SortOrder.None;
                }
            }
            else
            {
                direction = ListSortDirection.Ascending;
            }

            // Sort the selected column.
            gridProjects.Sort(newColumn, direction);
            newColumn.HeaderCell.SortGlyphDirection = direction == ListSortDirection.Ascending
                                                          ? SortOrder.Ascending
                                                          : SortOrder.Descending;
            RestoreSelectedRows();
            foreach (DataGridViewRow row in gridProjects.Rows)
            {
                SetStatusCell(row);
            }
        }

        /// <summary>
        /// Handles the CurrentCellDirtyStateChanged event of the GridProjects control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="EventArgs"/> instance containing the event data.
        /// </param>
        private void GridProjects_CurrentCellDirtyStateChanged(object sender, EventArgs e)
        {
            if (gridProjects.CurrentCell is DataGridViewCheckBoxCell)
            {
                gridProjects.CommitEdit(DataGridViewDataErrorContexts.Commit);
            }
        }

        /// <summary>
        /// Handles the DataBindingComplete event of the GridProjects control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="DataGridViewBindingCompleteEventArgs"/> instance containing the event data.
        /// </param>
        private void GridProjects_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
        {
            foreach (DataGridViewColumn column in gridProjects.Columns)
            {
                column.SortMode = DataGridViewColumnSortMode.Programmatic;
            }

            gridProjects.SelectionMode = DataGridViewSelectionMode.ColumnHeaderSelect;
        }

        /// <summary>
        /// Imports from file.
        /// </summary>
        /// <param name="file">
        /// The file.
        /// </param>
        private void ImportFromFile(string file)
        {
            XDocument document = XDocument.Parse(file, LoadOptions.SetLineInfo);
            OperationResult result = XmlParser.ParseInputFile(document);
            if (result.ErrorType != 0)
            {
                SetColoredText(Color.Red, "Error parsing input file: " + result.ErrorDescription, true);
                return;
            }

            SetColoredText(Color.Green, "Data was successfully imported.", true);
            var model = (XmlDocumentModel)result.Result;
            bool alreadyEdited = _needSave;
            bool alreadyContainsImportedData = cbxTarget.SelectedValue != null;
            if (!string.IsNullOrEmpty(model.SolutionPath))
            {
                txtSolutionFolder.Text = model.SolutionPath;
            }

            if (!string.IsNullOrEmpty(model.LogsPath))
            {
                txtLogsFolder.Text = model.LogsPath;
            }

            NeedSaveStatusChange(alreadyEdited || alreadyContainsImportedData);
            foreach (TargetSystem targetSystem in model.TargetSystems)
            {
                _targetSystemRepository.AddTargetSystem(targetSystem);
            }

            BindTargetSystems();
            _changedAfterDeployStart = true;
        }

        /// <summary>
        /// The main form_ form closing.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (_needSave)
            {
                if (MessageBoxEx.Show(
                    this, 
                    "Save changes?", 
                    "Unsaved changes detected", 
                    MessageBoxButtons.YesNo, 
                    MessageBoxIcon.Information) == DialogResult.Yes)
                {
                    var model = new XmlDocumentModel
                                    {
                                        LogsPath = PathesHelper.FixSolutionPath(txtLogsFolder.Text), 
                                        SolutionPath =
                                            PathesHelper.FixSolutionPath(txtSolutionFolder.Text), 
                                        TargetSystems = _targetSystemRepository.GetTargetSystems()
                                    };
                    string file = XmlParser.CreateOutputFile(model);

                    SetInitialPositionForSaveDialog(false);

                    if (dlgSaveFileDialog.ShowDialog(this) == DialogResult.OK)
                    {
                        string path = dlgSaveFileDialog.FileName;
                        try
                        {
                            File.WriteAllText(path, file);
                        }
                        catch (Exception ex)
                        {
                            SetColoredText(
                                Color.Red, 
                                string.Format("Can't save file {0}: {1}", path, ex.Message), 
                                true);
                            e.Cancel = true;
                        }
                    }
                    else
                    {
                        e.Cancel = true;
                    }
                }
            }
        }

        /// <summary>
        /// The need save status change.
        /// </summary>
        /// <param name="needChange">
        /// The need change.
        /// </param>
        private void NeedSaveStatusChange(bool needChange)
        {
            if (needChange)
            {
                _needSave = true;
                if (!Text.EndsWith("*"))
                {
                    Text = string.Concat(Text, "*");
                }
            }
            else
            {
                _needSave = false;
                if (Text.EndsWith("*"))
                {
                    Text = Text.Substring(0, Text.Length - 1);
                }
            }
        }

        /// <summary>
        ///     Restores the selected rows.
        /// </summary>
        private void RestoreSelectedRows()
        {
            if (_selectedProjectsNames == null || !_selectedProjectsNames.Any())
            {
                return;
            }

            foreach (DataGridViewRow row in gridProjects.Rows)
            {
                if (row.Index < 0)
                {
                    continue;
                }

                if (_selectedProjectsNames.Contains(row.Cells[2].Value.ToString()))
                {
                    DataGridViewCell chechboxCell = row.Cells[0];
                    chechboxCell.Value = true;
                }
            }
        }

        /// <summary>
        ///     Saves the current selected rows.
        /// </summary>
        private void SaveCurrentSelectedRows()
        {
            _selectedProjectsNames = new List<string>();
            foreach (DataGridViewRow row in gridProjects.Rows)
            {
                if (row.Index < 0)
                {
                    continue;
                }

                DataGridViewCell chechboxCell = row.Cells[0];
                if (chechboxCell.Value != null && (bool)chechboxCell.Value)
                {
                    _selectedProjectsNames.Add(row.Cells[2].Value.ToString());
                }
            }
        }

        /// <summary>
        /// Saves the file as.
        /// </summary>
        /// <param name="file">
        /// The file.
        /// </param>
        /// <param name="isGetSample">
        /// if set to <c>true</c> [is get sample].
        /// </param>
        private void SaveFileAs(string file, bool isGetSample)
        {
            SetInitialPositionForSaveDialog(isGetSample);
            if (dlgSaveFileDialog.ShowDialog(this) == DialogResult.OK)
            {
                string path = dlgSaveFileDialog.FileName;
                _lastUsedFilePath = path;
                try
                {
                    File.WriteAllText(path, file);
                    SetText(string.Format("File {0} was successfully written", path), true);
                    if (!isGetSample)
                    {
                        NeedSaveStatusChange(false);
                    }
                }
                catch (Exception ex)
                {
                    SetColoredText(Color.Red, string.Format("Can't save file {0}: {1}", path, ex.Message), true);
                }
            }
        }

        /// <summary>
        ///     Selects all checked rows.
        /// </summary>
        private void SelectAllCheckedRows()
        {
            foreach (DataGridViewRow row in gridProjects.Rows)
            {
                if (row.Index < 0)
                {
                    continue;
                }

                DataGridViewCell chechboxCell = row.Cells[0];
                if (chechboxCell.Value != null && (bool)chechboxCell.Value)
                {
                    row.DefaultCellStyle.BackColor = Color.LightCyan;
                    row.DefaultCellStyle.SelectionBackColor = Color.LightCyan;
                }
                else
                {
                    int devideResult = row.Index % 2;
                    row.DefaultCellStyle.BackColor = devideResult != 0
                                                         ? gridProjects.AlternatingRowsDefaultCellStyle.BackColor
                                                         : gridProjects.RowsDefaultCellStyle.BackColor;
                    row.DefaultCellStyle.SelectionBackColor = devideResult != 0
                                                                  ? gridProjects.AlternatingRowsDefaultCellStyle
                                                                        .SelectionBackColor
                                                                  : gridProjects.RowsDefaultCellStyle
                                                                        .SelectionBackColor;
                }

                // row.Selected = chechboxCell.Value == chechboxCell.TrueValue;
            }
        }

        /// <summary>
        /// Sets the colored text.
        /// </summary>
        /// <param name="color">
        /// The color.
        /// </param>
        /// <param name="text">
        /// The text.
        /// </param>
        /// <param name="addSpace">
        /// if set to <c>true</c> [add space].
        /// </param>
        private void SetColoredText(Color color, string text, bool addSpace)
        {
            int start = txtResults.TextLength;
            txtResults.AppendText(text);
            int end = txtResults.TextLength;

            // Text box may transform chars, so (end-start) != text.Length
            txtResults.Select(start, end - start);
            {
                txtResults.SelectionColor = color;

                // could set box.SelectionBackColor, box.SelectionFont too.
            }

            txtResults.SelectionLength = 0; // clear
            txtResults.AppendText(Environment.NewLine);
            if (addSpace)
            {
                txtResults.AppendText(Environment.NewLine);
            }
        }

        /// <summary>
        ///     The set initial position for open dialog.
        /// </summary>
        private void SetInitialPositionForOpenDialog()
        {
            if (!string.IsNullOrEmpty(_lastUsedFilePath))
            {
                string path = _lastUsedFilePath.Substring(0, _lastUsedFilePath.LastIndexOf('\\') + 1);
                string fileName = _lastUsedFilePath.Replace(path, string.Empty);
                dlgOpenFile.InitialDirectory = path;
                dlgOpenFile.FileName = fileName;
            }
            else
            {
                if (!string.IsNullOrEmpty(SolutionPath))
                {
                    dlgOpenFile.InitialDirectory = SolutionPath;
                    dlgOpenFile.FileName = string.Empty;
                }
            }
        }

        /// <summary>
        /// The set initial position for save dialog.
        /// </summary>
        /// <param name="isGetSample">
        /// The is get sample.
        /// </param>
        private void SetInitialPositionForSaveDialog(bool isGetSample)
        {
            if (!string.IsNullOrEmpty(_lastUsedFilePath))
            {
                string path = _lastUsedFilePath.Substring(0, _lastUsedFilePath.LastIndexOf('\\') + 1);
                string fileName = _lastUsedFilePath.Replace(path, string.Empty);
                dlgSaveFileDialog.InitialDirectory = path;
                dlgSaveFileDialog.FileName = isGetSample ? "Sample.xml" : fileName;
            }
            else
            {
                if (!string.IsNullOrEmpty(SolutionPath))
                {
                    dlgSaveFileDialog.InitialDirectory = SolutionPath;
                    dlgSaveFileDialog.FileName = isGetSample ? "Sample.xml" : "Export.xml";
                }
            }
        }

        /// <summary>
        /// Sets the inline results text.
        /// </summary>
        /// <param name="color">
        /// The color.
        /// </param>
        /// <param name="text">
        /// The text.
        /// </param>
        private void SetInlineResultsText(Color color, string text)
        {
            int start = txtResults.TextLength;
            txtResults.AppendText(text);
            int end = txtResults.TextLength;
            txtResults.Select(start, end - start);
            {
                txtResults.SelectionColor = color;

                // could set box.SelectionBackColor, box.SelectionFont too.
            }

            txtResults.SelectionLength = 0;
        }

        /// <summary>
        /// Sets the progress status.
        /// </summary>
        /// <param name="text">
        /// The text.
        /// </param>
        /// <param name="color">
        /// The color.
        /// </param>
        /// <param name="bold">
        /// if set to <c>true</c> [bold].
        /// </param>
        private void SetProgressStatus(string text, Color color, bool bold)
        {
            lblProgressStatus.Font = new Font(
                lblProgressStatus.Font, 
                bold ? FontStyle.Bold : FontStyle.Regular);
            lblProgressStatus.ForeColor = color;
            lblProgressStatus.Text = text;
        }

        /// <summary>
        /// The set status cell.
        /// </summary>
        /// <param name="row">
        /// The row.
        /// </param>
        private void SetStatusCell(DataGridViewRow row)
        {
            object projectName = row.Cells[2].Value;
            if (projectName == null)
            {
                return;
            }

            DeployableProject currentProject =
                CurrentTarget.DeployableProjects.First(p => p.ProjectName == projectName.ToString());
            var imageCell = row.Cells[1] as DataGridViewImageCell;
            if (imageCell == null)
            {
                return;
            }

            imageCell.Value = currentProject.ReadyForDeploy ? Resources.Ready : Resources.NotReady;
        }

        /// <summary>
        /// Sets the text.
        /// </summary>
        /// <param name="text">
        /// The text.
        /// </param>
        /// <param name="addSpace">
        /// if set to <c>true</c> [add space].
        /// </param>
        private void SetText(string text, bool addSpace)
        {
            string textToAppend = string.Concat(
                text, 
                Environment.NewLine, 
                addSpace ? Environment.NewLine : string.Empty);
            txtResults.AppendText(textToAppend);
        }

        /// <summary>
        /// Sets the total results.
        /// </summary>
        /// <param name="success">
        /// The success.
        /// </param>
        /// <param name="failure">
        /// The failure.
        /// </param>
        /// <param name="skipped">
        /// The skipped.
        /// </param>
        private void SetTotalResults(int success, int failure, int skipped)
        {
            int start = txtResults.TextLength;
            txtResults.AppendText(
                Environment.NewLine + Environment.NewLine + "==============Overall results=============="
                + Environment.NewLine);
            int end = txtResults.TextLength;

            // Text box may transform chars, so (end-start) != text.Length
            txtResults.Select(start, end - start);
            {
                txtResults.SelectionColor = Color.Blue;

                // could set box.SelectionBackColor, box.SelectionFont too.
            }

            txtResults.SelectionLength = 0; // clear
            txtResults.AppendText(Environment.NewLine);

            SetInlineResultsText(Color.Blue, "Deployment: ");
            SetInlineResultsText(Color.Green, string.Format("{0} succeeded; ", success));
            SetInlineResultsText(Color.Red, string.Format("{0} failed; ", failure));
            SetInlineResultsText(Color.Gray, string.Format("{0} skipped; ", skipped));
            SetInlineResultsText(Color.Blue, string.Format("Completed at {0}", DateTime.Now.ToString("U")));
        }

        /// <summary>
        /// The txt logs folder_ text changed.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void TxtLogsFolder_TextChanged(object sender, EventArgs e)
        {
            NeedSaveStatusChange(true);
            _changedAfterDeployStart = true;
        }

        /// <summary>
        /// Handles the TextChanged event of the TxtResults control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="EventArgs"/> instance containing the event data.
        /// </param>
        private void TxtResults_TextChanged(object sender, EventArgs e)
        {
            txtResults.SelectionStart = txtResults.Text.Length;
            txtResults.ScrollToCaret();
            txtResults.SelectionLength = 0;
        }

        /// <summary>
        /// Handles the TextChanged event of the TxtSolutionFolder control.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// The <see cref="EventArgs"/> instance containing the event data.
        /// </param>
        private void TxtSolutionFolder_TextChanged(object sender, EventArgs e)
        {
            string fixedPath = PathesHelper.FixSolutionPath(txtSolutionFolder.Text);
            txtLogsFolder.Text = PathesHelper.FixLogsPath(fixedPath, true);
            NeedSaveStatusChange(true);
            _changedAfterDeployStart = true;
        }

        #endregion
    }
}